#include <trax.hpp>
#include "RayTracer.h"
#include "atTriangle.h"
#include "atBVHNode.h"
#include "bvhStack.h"
#include "Ray.h"
#include "HitRecord.h"

namespace AtomTrace
{
    namespace RayTracer{
        const Ray CastRay(const Vector3 & iorigin, const Vector3 & dir, float bias)
        {
            //float bias = 0.01f;
            Ray res;
            res._dir = dir;
            res._o = iorigin + res._dir.GetNormalize()*bias;
            return res;
        }
/**/
        bool TraceRay(const Ray & iRay, const Scene & scene, HitRecord & hit)
        {
            return TraceBVH(iRay, hit);
            /*
            return TraceTriangles(iRay, scene.startTriAddr, scene.numTri, hit);
            bool isHit = false;
            atTriangle tempTri;

            for(int i=0; i<scene.numTri; i++)
            {
                tempTri.LoadFromGM(scene.startTriAddr + i * 11);
                isHit |= tempTri.Intersect(iRay, hit);

            }
            return isHit;
            */
        }
/**/

        bool TraceBVH(const Ray & iRay, HitRecord & hit, int * poNodeID)
        {
            bool isHit = false;
            int current = 0;
            atBVHNode cur_node;
            bvhStack stack;
            stack.Push(0);

            hit.incDir = iRay._dir;
            
            while(!stack.IsEmpty())
            {
                current = stack.Pop();
                //printf("current is %i\n", current);
                cur_node.LoadFromGM(current);
                float boxHit = 1000000.0f;
                if(cur_node.Intersect(iRay, boxHit))
                {
                    //printf("Hit node %i\n", current);
                    //if(poNodeID != 0)
                    //    *poNodeID = current;
                    //printf("node %i has %i triangles\n", current, cur_node.numTri);
                    if(cur_node.numTri < 0)
                    {
                    	// interio node
                        //printf("Pushing children %i and %i\n", cur_node.childID, cur_node.childID + 1);
                        if(!stack.Push(cur_node.childID + 1)) // store right child in the stack
                            printf("Error, stack full to store right child of node %i.\n", current);
                        if(!stack.Push(cur_node.childID))
                            printf("Error, stack full to store left child of node %i.\n", current);
                    }else
                    {
                    	// leaf node
                        if(boxHit > hit.t) continue; // ignore box intersection if it farther than existing geometry intersection
                        isHit |= TraceTriangles(iRay, cur_node.childID, cur_node.numTri, hit);
                    }
                }
            }
            return isHit;
        }

        bool TraceTriangles(const Ray & iRay, int firstTriAddr, int numTri, HitRecord & hit)
        {
            atTriangle tri;
            bool isHit = false;
            for(int i=0; i<numTri; i++)
            {
                //printf("Intersection test for triangle at %i\n", firstTriAddr + i * 11);
                tri.LoadFromGM(firstTriAddr + i * 11);
                isHit |= tri.Intersect(iRay, hit);
            }
            return isHit;
        }
    }
} //namespace AtomTrace
